package com.webchat.ugc.service.redpacket;

import com.webchat.common.constants.RedPacketConstants;
import com.webchat.common.enums.AccountRelationTypeEnum;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.RoleCodeEnum;
import com.webchat.common.enums.payment.PaymentTransEventEnum;
import com.webchat.common.enums.payment.PaymentTransTypeEnum;
import com.webchat.common.exception.BusinessException;
import com.webchat.common.service.RedisService;
import com.webchat.common.util.ThreadPoolExecutorUtil;
import com.webchat.domain.dto.payment.PaymentTransRequestDTO;
import com.webchat.domain.vo.request.SendRedPacketRequestVO;
import com.webchat.domain.vo.response.RedPacketBaseVO;
import com.webchat.domain.vo.response.RedPacketDetailVO;
import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
import com.webchat.ugc.repository.dao.IRedPacketRecordDAO;
import com.webchat.ugc.repository.entity.RedPacketRecordEntity;
import com.webchat.ugc.service.AccountService;
import com.webchat.ugc.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;


public abstract class AbstractOpenRedPacketService implements RedPacketOpenInter {

    @Autowired
    public RedisService redisService;
    @Autowired
    private RedPacketService redPacketService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private IRedPacketRecordDAO redPacketRecordDAO;

    /**
     * 实际红包拆分（保证并发数据安全）
     * 具体逻辑由实现类实现：分布式锁实现、LUA脚本原子操作、预计算+队列
     *
     * @param redPacket
     * @param userId
     * @return
     */
    public abstract int openRedPacket(RedPacketBaseVO redPacket, String userId);

    /**
     * 拆包权限校验：
     * 1. 红包是否有效（是否真实存在、未失效、结束）
     * 2. 校验当前拆包人是否由拆包权限
     *    2.1 一对一：当前人是否为红包的接收人
     *    2.2 群聊多人：校验当前用户是否在群组内
     * 3. 校验当前用户是否拆过红包（每个人只能拆一次）
     *
     * ===> 获得拆包资格
     *
     * @param redPacket
     * @param userId
     */
    private void validateOpenPermission(RedPacketBaseVO redPacket, String userId) {

        /**
         * 红包是否有效（是否真实存在、未失效、结束）
         */
        Assert.notNull(redPacket, "红包不存在！");
        int status = redPacket.getStatus();
        if (RedPacketConstants.RedPacketStatus.EXPIRED.getStatus() == status) {
            throw new BusinessException("红包已过期");
        }
        if (RedPacketConstants.RedPacketStatus.END.getStatus() == status) {
            throw new BusinessException("来晚了～");
        }
        Assert.isTrue(RedPacketConstants.RedPacketStatus.RUNNING.getStatus() == status, "红包状态异常！");
        Assert.isTrue(redPacket.getExpireTime() > System.currentTimeMillis(), "红包已过期");

        /**
         *  2. 校验当前拆包人是否由拆包权限
         *    2.1 一对一：当前人是否为红包的接收人
         *    2.2 群聊多人：校验当前用户是否在群组内
         */
        String receiverId = redPacket.getReceiver();
        UserBaseResponseInfoVO receiver = accountService.accountInfo(receiverId);
        Assert.notNull(receiver, "红包接受账号不存在");
        if (RoleCodeEnum.isUserRole(receiver.getRoleCode())) {
            Assert.isTrue(receiver.getUserId().equals(userId), "没有拆包权限");
        } else if (RoleCodeEnum.GROUP.getCode().equals(receiver.getRoleCode())) {
            boolean isGroupUser = accountService.isSubscribe(userId, receiverId, AccountRelationTypeEnum.USER_GROUP);
            Assert.isTrue(isGroupUser, "没有拆包权限");
        }

        /**
         * 3. 校验当前用户是否拆过红包（每个人只能拆一次）
         */
        boolean isOpen = this.isOpenRedPacket(redPacket.getId(), userId);
        Assert.isTrue(!isOpen, "重复拆分");
    }

    @Transactional
    @Override
    public int open(Long redPacketId, String userId) {

        RedPacketDetailVO redPacket = redPacketService.getRedPacket(redPacketId);
        /**
         * 1. 校验红包拆分权限
         * 包含：红包有效、拆包权限 ……
         */
        this.validateOpenPermission(redPacket, userId);

        /**
         * 2. 获得拆包资格，拆红包
         */
        int amount = this.openRedPacket(redPacket, userId);
        BigDecimal openYun = new BigDecimal(amount).movePointLeft(2).setScale(2, RoundingMode.HALF_UP);
        /**
         * 3. 业务侧持久化拆分数据
         */
        RedPacketRecordEntity redPacketRecord = new RedPacketRecordEntity();
        redPacketRecord.setRedPacketId(redPacketId);
        redPacketRecord.setUserId(userId);
        redPacketRecord.setMoney(openYun);
        redPacketRecord.setCreateDate(new Date());
        redPacketRecordDAO.save(redPacketRecord);
        /**
         * 4. 调用支付平台（webchat-pay 微服务，完成用户拆包金额入账）
         */
        String orderId = redPacket.getOrderId();
        PaymentTransRequestDTO paymentTransRequest = this.buildPaymentTransRequestDTO(orderId, userId, openYun);
        boolean transResult = paymentService.doTrans(paymentTransRequest);
        if (!transResult) {
            // TODO 回滚本次红包拆分消耗数据(回滚Redis缓存数据)
            throw new BusinessException("服务繁忙");
        }
        return amount;
    }

    public boolean updateRedPacketStatus(Long redPacketId, RedPacketConstants.RedPacketStatus status) {

        return redPacketService.updateRedPacketStatus(redPacketId, status);
    }

    /**
     * 添加{userId}加入到红包拆包用户redis缓存set集合
     * @param redPacketId
     * @param userId
     */
    public void addUser2OpenRedPacketUsersCache(Long redPacketId, String userId) {
        String cacheKey = RedisKeyEnum.RED_PACKET_OPEN_USERS.getKey(String.valueOf(redPacketId));
        redisService.sadd(cacheKey, userId);
        redisService.expire(cacheKey, RedisKeyEnum.RED_PACKET_OPEN_USERS.getExpireTime());
    }

    /**
     * 判断{userId}是否拆分过红包{redPacketId}
     * @param redPacketId
     * @param userId
     * @return
     */
    private boolean isOpenRedPacket(Long redPacketId, String userId) {
        String cacheKey = RedisKeyEnum.RED_PACKET_OPEN_USERS.getKey(String.valueOf(redPacketId));
        return redisService.sIsMember(cacheKey, userId);
    }

    /**
     * 刷新红包状态
     *
     * @param redPacketId
     * @param status
     */
    protected void updateRedPacketStatusCache(Long redPacketId, RedPacketConstants.RedPacketStatus status) {

        redPacketService.updateRedPacketStatus(redPacketId, status);
    }

    private PaymentTransRequestDTO buildPaymentTransRequestDTO(String orderId, String userId, BigDecimal openYun) {
        PaymentTransRequestDTO dto = new PaymentTransRequestDTO();
        dto.setOrderId(orderId);
        dto.setAmount(openYun);
        dto.setBillType(PaymentTransTypeEnum.INCOME.getTransType());
        dto.setTransDetail("拆分红包");
        dto.setTransEvent(PaymentTransEventEnum.RED_PACKET.getTransEvent());
        dto.setSourceUserId(userId);
        return dto;
    }
}
